package com.kevin.mapping.script.node.stmt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.kevin.mapping.script.engine.JSONEnv;
import com.kevin.mapping.script.exception.ExecuteException;
import com.kevin.mapping.script.lexer.Path;
import com.kevin.mapping.script.lexer.PathValue;
import com.kevin.mapping.script.lexer.Token;

public class AssignStmt extends Stmt {
    private Stmt path;
    private Stmt value;

    public AssignStmt(Token token, Stmt path, Stmt value) {
        super(token);
        this.path = path;
        this.value = value;
    }

    public Stmt getPath() {
        return path;
    }

    public void setPath(Stmt path) {
        this.path = path;
    }

    public Token getValue(JSONEnv env) {
        PathValue pathValue = (PathValue) path.getToken();
        Path[] paths = pathValue.getPaths();
        Object value = this.value.getValue(env);
        if (value != null) {
            assign(paths, env.getData(), 0, paths.length - 1, value);
        }
        return null;
    }

    private void assign(Path[] paths, Object json, int startIndex, int endIndex, Object value) {
        JSON object = (JSONObject) json;
        for (int i = startIndex; i < endIndex; i++) {
            Path path = paths[i];
            if (object instanceof JSONArray) {
                JSONArray array = (JSONArray) object;
                for (int j = 0; j < array.size(); j++) {
                    JSONObject jsonObject = array.getJSONObject(j);
                    assign(paths, jsonObject, i, endIndex, value);
                }
                return;
            } else {
                Object o = ((JSONObject) object).get(path.getPath());
                if (o == null) {
                    if (path.isArray()) {
                        o = new JSONArray();
                    } else {
                        o = new JSONObject();
                    }
                    ((JSONObject) object).put(path.getPath(), o);
                    object = (JSON) o;
                } else {
                    object = (JSON) JSON.toJSON(o);
                }
            }
            if (!(object instanceof JSONArray)) {
                if (path.isArray()) {
                    throw new ExecuteException("实际数据格式是对象，期望是数组. 数据=" + JSON.toJSONString(object));
                }
                continue;
            }
            JSONArray array = (JSONArray) object;
            if (!path.isArray()) {
                if (array.isEmpty()) {
                    object = new JSONObject();
                    array.add(object);
                    continue;
                } else {
                    object = array;
                    continue;
                }
            }
            if (array.isEmpty()) {
                JSONArray list = new JSONArray();
                int a = path.getStartIndex();
                for (; a < path.getEndIndex(); a++) {
                    list.add(new JSONObject());
                }
                object = list;
                continue;
            }
            if (array.size() > path.getEndIndex()) {
                if (path.getStartIndex().equals(path.getEndIndex())) {
                    object = array.getJSONObject(path.getStartIndex());
                } else {
                    object = (JSON) JSON.toJSON(array.subList(path.getStartIndex(), path.getEndIndex()));
                }
            } else {
                JSONArray list = new JSONArray();
                int a = path.getStartIndex();
                for (; a < array.size() && a < path.getEndIndex(); a++) {
                    list.add(array.getJSONObject(a));
                }
                for (; a < path.getEndIndex(); a++) {
                    list.add(new JSONObject());
                }
                object = list;
            }
        }
        Path lastPath = paths[endIndex];

        if (!(object instanceof JSONArray)) {
            ((JSONObject) object).put(lastPath.getPath(), value);
            return;
        }
        JSONArray array = ((JSONArray) object);
        for (int i = 0; i < array.size(); i++) {
            JSONObject jsonObject = array.getJSONObject(i);
            Object data = jsonObject.get(lastPath.getPath());
            if (null == data) {
                jsonObject.put(lastPath.getPath(), value);
                continue;
            }
            if (data instanceof JSONArray) {
                JSONArray datas = (JSONArray) data;
                if (!lastPath.isArray()) {
                    datas.add(value);
                    continue;
                }
                for (int b = lastPath.getStartIndex(); b < lastPath.getEndIndex(); b++) {
                    if (i < array.size()) {
                        array.add(b, value);
                    } else {
                        array.add(value);
                    }
                }
            } else {
                jsonObject.put(lastPath.getPath(), value);
            }
        }
    }

    public void setValue(Stmt value) {
        this.value = value;
    }

    @Override
    public Token execute(JSONEnv env) {
        return getValue(env);
    }
}
